package main

import (
	"fmt"
	"time"
)

func worker(die chan bool, index int) {
	fmt.Println("Begin: ", index)
	for {
		select {
		//case xx：
		//做事的分支
		case val, ok := <-die:
			if ok == true {
				fmt.Printf("Done: %v, val: %v\n", index, val)
				die <- true
			} else {
				fmt.Printf("Done: %v, close\n", index)
			}
			return
		}
	}
}

func test_event() {
	die := make(chan bool)

	for i := 1; i <= 3; i++ {
		go worker(die, i)
	}

	time.Sleep(time.Second * 2)
	die <- false
	time.Sleep(time.Second * 2)
	fmt.Println("after sleep 2s")
	<-die
	close(die)
	select {} //deadlock we expected
}

func test_operate_on_closed_channel() {
	cb := make(chan bool)
	close(cb)
	x := <-cb
	fmt.Printf("%#v\n", x)

	x, ok := <-cb
	fmt.Printf("%#v %#v\n", x, ok)

	ci := make(chan int)
	close(ci)
	y, ok := <-ci
	fmt.Printf("val:%#v, ok:%#v\n", y, ok)

	cb <- true
}

func test_buffered_channel() {
	c := make(chan int, 2)
	c <- 15
	c <- 34
	c <- 65
	close(c)
	fmt.Printf("%d\n", <-c)
	fmt.Printf("%d\n", <-c)
	fmt.Printf("%d\n", <-c)
	fmt.Printf("%d\n", <-c)

	c <- 1
}

func test_buffered_channel_with_goroutine() {
	c := make(chan int, 1)
	go func() {
		for i := 0; i < 3; i++ {
			c <- i
			fmt.Println("insert: %v", i)
		}
		close(c)
	}()
	fmt.Printf("%d\n", <-c)
	time.Sleep(time.Second * 2)
	fmt.Printf("%d\n", <-c)
	fmt.Printf("%d\n", <-c)
	fmt.Printf("%d\n", <-c)
}

func generator(strings chan string) {
	strings <- "Five hour's New York jet lag"
	strings <- "and Cayce Pollard wakes in Camden Town"
	time.Sleep(time.Second * 2)
	strings <- "to the dire and ever-decreasing circles"
	strings <- "of disrupted circadian rhythm."
	close(strings)
}

func test_range_chan() {
	strings := make(chan string)
	go generator(strings)

	//range will wait until the channel closed
	for s := range strings {
		fmt.Printf("%s\n", s)
	}
	fmt.Printf("\n")
}

func newUniqueIDService() <-chan string {
	id := make(chan string)
	go func() {
		var counter int64 = 0
		for {
			fmt.Printf("gen: %x\n", counter)
			id <- fmt.Sprintf("%x", counter)
			counter += 1
		}
	}()
	return id
}

func test_uniq_id() {
	id := newUniqueIDService()
	for i := 0; i < 3; i++ {
		fmt.Println(<-id)
	}
}

func test_nil_channel() {
	var c1, c2 chan int = make(chan int), make(chan int)
	go func() {
		time.Sleep(time.Second * 5)
		c1 <- 5
		close(c1)
	}()

	go func() {
		time.Sleep(time.Second * 7)
		c2 <- 7
		close(c2)
	}()

	for {
		select {
		case x, ok := <-c1:
			if !ok {
				// nil channel will block forever
				c1 = nil
			} else {
				fmt.Println(x)
			}
		case x, ok := <-c2:
			if !ok {
				c2 = nil
			} else {
				fmt.Println(x)
			}
		}
		if c1 == nil && c2 == nil {
			break
		}
	}
	fmt.Println("over")
}

func test_timeout_heartbeat() {
	heartbeat := time.Tick(2 * time.Second)
	timeout := time.After(5 * time.Second)
	for {
		select {
		case <-heartbeat:
			fmt.Println("heartbeat..")
		case <-timeout:
			fmt.Println("timeout..")
			return
		}
	}
}

func fab_worker(ch, ch2 chan int) {
	pre := 0

	for {
//		time.Sleep(time.Second * 1)
		select {
		case n, ok := <-ch:
			if ok {
				if pre == 0 {
					pre = n
					fmt.Printf("%v\n", n)
				} else {
					if n > 10000 {
						fmt.Printf("over 10000, will exit\n")
						close(ch2)
						return
					} else {
						fmt.Printf("insert: %v\n", n + pre)
						ch2 <- (n + pre)
						pre = n
					}
				}
			} else {
				fmt.Println("channel1 closed, exit")
				ch = nil
				return
			}
		}
	}
}

func fabr() {
	ch := make(chan int)
	ch2 := make(chan int)

	go fab_worker(ch, ch2)

	ch <- 2
	ch <- 3
	for {
		select {
		case n, ok := <-ch2:
			if ok {
				ch <- n
			} else {
				fmt.Println("channel2 closed, exit")
				return
			}
		}
	}
}

func main() {
	//test_event()
	//test_operate_on_closed_channel()
	//test_buffered_channel()
	//test_buffered_channel_with_goroutine()
	//test_range_chan()
	//test_uniq_id()
	//test_nil_channel()
	//test_timeout_heartbeat()
	fabr()
}
